﻿/*
Copyright (c) 2009 Vratislav Cermak (vratacermak@gmail.com)
 
This file is part of TeachMe Project. See http://code.google.com/p/teach-me for more information.
 
TeachMe is an open source project. Using of TeachMe is free of charge.
It is distributed under GNU General Public License version 3 (GPLv3).
GPLv3 can be found at http://www.gnu.org/licenses/gpl.html.
 
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using TeachMe.Application.Properties;

namespace TeachMe.Application
{
    public class SubtitlesImporter
    {
        private readonly Dictionary<string, Sentence> _result = new Dictionary<string, Sentence>();
        private readonly ThreadWorkerData _worker;
        private readonly ImportContext _context;

        public SubtitlesImporter(ThreadWorkerData worker, ImportContext context)
        {
            _worker = worker;
            _context = context;
        }

        private bool CanImportWord(string word)
        {
            return _context.ImportWordDelegate(_context, word);
        }

        public void Perform()
        {
            Dictionary<int, Sentence> sentences = ExtractSentences(_context.Files, _context.CodeEncoding);

            _worker.UpdateStatus(Resources.ImportStatusExtractingWords);

            foreach (KeyValuePair<int, Sentence> pair in sentences)
            {
                if (_worker.Canceled)
                {
                    break;
                }

                string sentence = pair.Value.Text;
                sentence = Regex.Replace(sentence, "[^\\w-']", " ");
                sentence = Regex.Replace(sentence, "[ ]+", " ");
                sentence = sentence.ToLower().Trim();

                string[] parts = sentence.Split(new [] {' '});

                foreach (string word in parts)
                {
                    string modifiedWord = word;

                    modifiedWord = Regex.Replace(modifiedWord, "^[\\W]+", "");
                    modifiedWord = Regex.Replace(modifiedWord, "[\\W]+$", "");
                    
                    if (!CanImportWord(modifiedWord))
                    {
                        continue;
                    }
                    if (_result.ContainsKey(modifiedWord))
                    {
                        continue;
                    }

                    _result.Add(modifiedWord, pair.Value);
                }
            }

            Logging.Debug(GetType(), "Got '{0}' words.", _result.Count);
        }

        private Dictionary<int, Sentence> ExtractSentences(IEnumerable<string> files, string encoding)
        {
            Dictionary<int, Sentence> sentences = new Dictionary<int, Sentence>();

            foreach (string file in files)
            {
                if (_worker.Canceled)
                {
                    break;
                }

                Logging.Debug(GetType(), "Extracting sentences from: {0}", file);

                _worker.UpdateStatus(String.Format(Resources.ImportStatusReadingFile, Path.GetFileName(file)));

                Sentence currentSentence = new Sentence();

                using (TextReader reader = new StreamReader(file, Encoding.GetEncoding(encoding)))
                {
                    while (true)
                    {
                        string line = reader.ReadLine();
                        if (line == null)
                        {
                            break;
                        }

                        if (String.IsNullOrEmpty(line))
                        {
                            continue;
                        }
                        if (Regex.IsMatch(line, "[\\d]+"))
                        {
                            continue;
                        }
                        if (Regex.IsMatch(line, "^[\\d :,.->]+$"))
                        {
                            continue;
                        }

                        line = Regex.Replace(line, "<[^>]*>", "");
                        line = Regex.Replace(line, "{[^}]*}", "");
                        line = Regex.Replace(line, "\"", "");
                        line = Regex.Replace(line, "[.]+", ".");
                        line = Regex.Replace(line, "[?]+", "?");
                        line = Regex.Replace(line, "[!]+", "!");
                        line = line.Trim();

                        int start = 0;
                        for (int idx = 0; idx < line.Length; idx++)
                        {
                            if (line[idx] == '.' || line[idx] == '!' || line[idx] == '?')
                            {
                                currentSentence.Add(line.Substring(start, (idx - start) + 1));

                                if (IsSentence(currentSentence.Text))
                                {
                                    if (!sentences.ContainsKey(currentSentence.GetHashCode()))
                                    {
                                        sentences.Add(currentSentence.GetHashCode(), currentSentence);
                                    }
                                    currentSentence = new Sentence();
                                }

                                start = idx + 1;
                            }
                        }
                        if (start < line.Length)
                        {
                            currentSentence.Add(line.Substring(start, line.Length - start));
                        }
                    }
                    if (IsSentence(currentSentence.Text) && !sentences.ContainsKey(currentSentence.GetHashCode()))
                    {
                        sentences.Add(currentSentence.GetHashCode(), currentSentence);
                    }
                }
            }

            Logging.Debug(GetType(), "Got '{0}' sentences.", sentences.Count);

            return sentences;
        }

        private static bool IsSentence(string sentence)
        {
            if (String.IsNullOrEmpty(sentence))
            {
                return false;
            }

            if (sentence.Length <= 2)
            {
                return false;
            }

            string lastWord = GetLastWord(sentence);

            if (lastWord.Length < 2)
            {
                return false;
            }

            if (sentence.EndsWith("."))
            {
                string[] exceptions = {"mr", "ms", "mrs"};

                lastWord = lastWord.ToLower();

                foreach (string exception in exceptions)
                {
                    if (lastWord.EndsWith(exception))
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        private static string GetLastWord(string sentence)
        {
            char[] separators = { ' ', '.', '!', '?' };
            sentence = sentence.TrimEnd(separators);
            string lastWord = sentence.LastIndexOfAny(separators) >= 0 ? sentence.Substring(sentence.LastIndexOfAny(separators)) : sentence;
            return lastWord.Trim();
        }

        public Dictionary<string, Sentence> Result
        {
            get
            {
                return _result;
            }
        }
    }
}
